package com.slipper.sfts.stock.service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import com.slipper.common.util.BeanUtil;
import com.slipper.common.util.StringUtil;
import com.slipper.qmt.constants.Constants;
import com.slipper.qmt.vo.DealVO;
import com.slipper.qmt.vo.OrderResultVO;
import com.slipper.qmt.vo.OrderVO;
import com.slipper.sfts.api.member.bo.MemberBrokerAccountBO;
import com.slipper.sfts.api.stock.bo.StockDealBO;
import com.slipper.sfts.api.stock.bo.StockPositionBO;
import com.slipper.sfts.member.service.MemberBrokerAccountService;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import com.slipper.sfts.stock.dao.StockOrderDao;
import com.slipper.sfts.api.stock.bo.StockOrderBO;

import com.slipper.common.transaction.ReadTransactional;
import com.slipper.common.transaction.WriteTransactional;
import com.slipper.common.util.Tool;

@Service
@ReadTransactional
public class StockOrderService {
    private static final Logger logger = LoggerFactory.getLogger(StockOrderService.class);

    @Resource
    private StockOrderDao stockOrderDao;
    @Resource
    private StockDealService stockDealService;
    @Resource
    private StockPositionService stockPositionService;
    @Resource
    private MemberBrokerAccountService memberBrokerAccountService;
    @Resource
    private StockOrderMessageService stockOrderMessageService;
    
    public StockOrderBO queryBy(String id) {
        return stockOrderDao.queryBy(id);
    }
    
    public List<StockOrderBO> queryList(StockOrderBO bo) {
        return stockOrderDao.queryList(bo);
    }
    
    public PageInfo<StockOrderBO> queryPage(StockOrderBO bo) {
        PageHelper.startPage(bo.getPageNumber(), bo.getPageSize());
        return new PageInfo<>(stockOrderDao.queryList(bo));
    }

    @WriteTransactional
    public void insert(StockOrderBO bo) {
        stockOrderDao.insert(bo);
    }
    
    @WriteTransactional
    public int save(StockOrderBO bo) {
        if (bo.getId() != null && !"".equals(bo.getId())) {
            return stockOrderDao.update(bo);
        } else {
            bo.setId(Tool.getUUID());
            return stockOrderDao.insert(bo);
        }
    }
    
    @WriteTransactional
    public int delete(StockOrderBO bo) {
        return stockOrderDao.delete(bo);
    }

    public StockOrderBO queryByOrderSysId(String sysOrderId) {
        return stockOrderDao.queryByOrderSysId(sysOrderId);
    }

    @WriteTransactional
    public void dealOrderResult(OrderResultVO vo) {
        StockOrderBO stockOrder = stockOrderDao.lockBy(vo.getStockOrderId());
        if (stockOrder == null) return;

        if (vo.getOrderId() == -1) {
            stockOrder.setExchangeCode(vo.getExchangeId());
            stockOrder.setExchangeName(vo.getExchangeName());
            stockOrder.setName(vo.getInstrumentName());
            stockOrder.setOrderId(vo.getOrderId());
            stockOrder.setStatus(StockOrderBO.STATUS_JUNK);
            stockOrder.setFinished(2);
            stockOrderDao.update(stockOrder);

            String message = stockOrder.getCode() + "[" + stockOrder.getName() + "]委托失败。";
            stockOrderMessageService.save(stockOrder.getMemberId(), stockOrder.getAccountId(), message);
        } else {
            stockOrder.setOrderId(vo.getOrderId());
            stockOrder.setExchangeCode(vo.getExchangeId());
            stockOrder.setExchangeName(vo.getExchangeName());
            stockOrder.setName(vo.getInstrumentName());
            stockOrder.setOrderId(vo.getOrderId());
            stockOrder.setStatus(StockOrderBO.STATUS_REPORTED);
            stockOrderDao.update(stockOrder);
        }
    }

    @WriteTransactional
    public void dealOrder(OrderVO vo) {
        if (StringUtil.isBlank(vo.getOrderRemark())) return;

        switch (vo.getOrderStatus()) {
            case Constants.OrderStatus.ENTRUST_STATUS_REPORTED: // 已报
                dealReported(vo);
                break;
            case Constants.OrderStatus.ENTRUST_STATUS_REPORTED_CANCEL: // 已报待撤
                break;
            case Constants.OrderStatus.ENTRUST_STATUS_PARTSUCC_CANCEL: // 部成待撤
                break;
            case Constants.OrderStatus.ENTRUST_STATUS_PART_CANCEL: // 部撤
                dealPartCancel(vo);
                break;
            case Constants.OrderStatus.ENTRUST_STATUS_CANCELED: // 已撤
                dealCancel(vo);
                break;
             case Constants.OrderStatus.ENTRUST_STATUS_PART_SUCC: // 部成，修改状态
                dealPartSuccess(vo);
                break;
            case Constants.OrderStatus.ENTRUST_STATUS_SUCCEEDED: // 已成，修改状态，解冻金额
                dealSuccess(vo);
                break;
            case Constants.OrderStatus.ENTRUST_STATUS_JUNK: // 废单
                dealJunk(vo);
                break;
            case Constants.OrderStatus.ENTRUST_STATUS_DETERMINED: // 已确认
            case Constants.OrderStatus.ENTRUST_STATUS_UNKNOWN: // 未知
                logger.error("未知状态的委托单回调：{}", vo);
                break;
        }
    }

    // 上报成功，修改委托号、冻结金额、可用金额，如有持仓冻结持仓
    @WriteTransactional
    public void dealReported(OrderVO vo) {
        StockOrderBO stockOrder = stockOrderDao.lockBy(vo.getOrderRemark());
        if (stockOrder == null || !StockOrderBO.STATUS_SUBMIT.equals(stockOrder.getStatus())) return;

        stockOrder.setOrderSysId(vo.getOrderSysId());
        stockOrder.setExchangeCode(vo.getExchangeId());
        stockOrder.setExchangeName(vo.getExchangeName());
        stockOrder.setName(vo.getInstrumentName());
        stockOrder.setStatus(StockOrderBO.STATUS_REPORTED);
        stockOrder.setFreezeMoney(new BigDecimal(0));
        stockOrder.setFreezeCommission(new BigDecimal(0));

        if (StockOrderBO.ORDER_TYPE_BUY == stockOrder.getOrderType()) {
            // 买入时冻结金额
            BigDecimal freezeMarin = stockOrder.getPrice().multiply(BigDecimal.valueOf(stockOrder.getVolume()));
            stockOrder.setFreezeCommission(calcCommission(
                    stockOrder.getMemberId(),
                    stockOrder.getBrokerAccountId(),
                    freezeMarin));
            stockOrder.setFreezeMoney(freezeMarin);

            // 冻结金额
            memberBrokerAccountService.freezeMoney(
                    stockOrder.getMemberId(),
                    stockOrder.getBrokerAccountId(),
                    stockOrder.getFreezeMoney().add(stockOrder.getFreezeCommission()));
        }
        stockOrderDao.update(stockOrder);

        if (StockOrderBO.ORDER_TYPE_SELL == stockOrder.getOrderType()) {
            // 如果为卖出且有持仓则冻结持仓
            stockPositionService.freezeVolume(stockOrder.getMemberId(),
                    stockOrder.getBrokerAccountId(),
                    stockOrder.getCode(),
                    stockOrder.getVolume());
        }

        String message = stockOrder.getCode() + "[" + stockOrder.getName() + "]委托成功。";
        stockOrderMessageService.save(stockOrder.getMemberId(), vo.getAccountId(), message);
    }

    // 部撤，修改已撤数量
    @WriteTransactional
    public void dealPartCancel(OrderVO vo) {
        StockOrderBO stockOrder = stockOrderDao.lockBy(vo.getOrderRemark());
        if (stockOrder == null) return;

        if (stockOrder.getCancelVolume() == null) {
            stockOrder.setCancelVolume(0);
        }

        int volume = vo.getTradedVolume() != null ? vo.getTradedVolume() : 0;
        stockOrder.setCancelVolume(stockOrder.getCancelVolume() + volume);
        stockOrder.setStatus(StockOrderBO.STATUS_PART_CANCEL);
        stockOrderDao.update(stockOrder);
    }

    // 已撤，成交反向操作
    public void dealCancel(OrderVO vo) {
        StockOrderBO stockOrder = stockOrderDao.lockBy(vo.getOrderRemark());
        if (stockOrder == null) return;

        if (stockOrder.getCancelVolume() == null) {
            stockOrder.setCancelVolume(0);
        }

        int cancelVolume = vo.getTradedVolume() != null ? vo.getTradedVolume() : 0;
        stockOrder.setCancelVolume(stockOrder.getCancelVolume() + cancelVolume);
        stockOrder.setStatus(StockOrderBO.STATUS_CANCELED);
        stockOrder.setFinished(3);
        stockOrderDao.update(stockOrder);

        if (StockOrderBO.ORDER_TYPE_BUY == stockOrder.getOrderType()) {
            // 修改会员冻结金额、可用金额
            memberBrokerAccountService.unfreezeMoney(
                    stockOrder.getMemberId(),
                    stockOrder.getBrokerAccountId(),
                    stockOrder.getFreezeMoney().add(stockOrder.getFreezeCommission()));
        }

        // 如果为卖出且有持仓则解冻持仓
        if (StockOrderBO.ORDER_TYPE_SELL == stockOrder.getOrderType()) {
            stockPositionService.unfreezeVolume(
                    stockOrder.getMemberId(),
                    stockOrder.getBrokerAccountId(),
                    stockOrder.getCode(),
                    stockOrder.getCancelVolume());
        }

        String message = stockOrder.getCode() + "[" + stockOrder.getName() + "]撤消委托成功。";
        stockOrderMessageService.save(stockOrder.getMemberId(), vo.getAccountId(), message);
    }

    // 部成，仅修改状态
    @WriteTransactional
    public void dealPartSuccess(OrderVO vo) {
        StockOrderBO stockOrder = stockOrderDao.lockBy(vo.getOrderRemark());
        if (stockOrder == null) return;

        stockOrder.setStatus(StockOrderBO.STATUS_PART_SUCC);
        stockOrderDao.update(stockOrder);
    }

    // 已成，修改状态，解冻金额
    @WriteTransactional
    public void dealSuccess(OrderVO vo) {
        StockOrderBO stockOrder = stockOrderDao.lockBy(vo.getOrderRemark());
        if (stockOrder == null) return;

        stockOrder.setStatus(StockOrderBO.STATUS_SUCCEEDED);
        stockOrder.setFinished(1);
        stockOrderDao.update(stockOrder);

        if (StockOrderBO.ORDER_TYPE_BUY == stockOrder.getOrderType()) {
            // 解冻金额
            memberBrokerAccountService.unfreezeMoney(
                    stockOrder.getMemberId(),
                    stockOrder.getBrokerAccountId(),
                    stockOrder.getFreezeMoney().add(stockOrder.getFreezeCommission()));
        }
    }

    // 废单，修改状态为废单，结束订单
    @WriteTransactional
    public void dealJunk(OrderVO vo) {
        StockOrderBO stockOrder = stockOrderDao.lockBy(vo.getOrderRemark());
        if (stockOrder == null) return;

        stockOrder.setExchangeCode(vo.getExchangeId());
        stockOrder.setExchangeName(vo.getExchangeName());
        stockOrder.setName(vo.getInstrumentName());
        stockOrder.setCancelReason(vo.getStatusMsg());
        stockOrder.setStatus(StockOrderBO.STATUS_JUNK);
        stockOrder.setFinished(2);
        stockOrderDao.update(stockOrder);

        String message = stockOrder.getCode() + "[" + stockOrder.getName() + "]委托失败："  + vo.getStatusMsg();
        stockOrderMessageService.save(stockOrder.getMemberId(), vo.getAccountId(), message);
    }

    @WriteTransactional
    public void dealDeal(DealVO vo) {
        if (StringUtil.isBlank(vo.getOrderRemark())) {
            StockDealBO stockDeal = BeanUtil.copy(vo, StockDealBO.class);
            stockDeal.setCode(vo.getInstrumentId());
            stockDeal.setName(vo.getInstrumentName());
            stockDealService.save(stockDeal);
            return;
        }

        StockOrderBO stockOrder = stockOrderDao.lockBy(vo.getOrderRemark());
        if (stockOrder == null) {
            StockDealBO stockDeal = BeanUtil.copy(vo, StockDealBO.class);
            stockDeal.setCode(vo.getInstrumentId());
            stockDeal.setName(vo.getInstrumentName());
            stockDealService.save(stockDeal);
            return;
        }

        // 修改成交金额，成交数量，成交价
        if (stockOrder.getVolume() == null) {
            stockOrder.setVolume(0);
        }
        if (stockOrder.getTradedVolume() == null) {
            stockOrder.setTradedVolume(0);
        }
        if (stockOrder.getTradedAmount() == null) {
            stockOrder.setTradedAmount(new BigDecimal(0));
        }

        int volumeTraded = vo.getTradedVolume() != null ? vo.getTradedVolume() : 0;
        stockOrder.setTradedVolume(stockOrder.getTradedVolume() + volumeTraded);
        stockOrder.setTradedPrice(vo.getTradedPrice().setScale(2, RoundingMode.HALF_UP));
        stockOrder.setTradedAmount(stockOrder.getTradedAmount()
                .add(vo.getTradedPrice().multiply(BigDecimal.valueOf(vo.getTradedVolume())))
                .setScale(2, RoundingMode.HALF_UP));
        stockOrderDao.update(stockOrder);

        StockDealBO stockDeal = BeanUtil.copy(vo, StockDealBO.class);
        stockDeal.setMemberId(stockOrder.getMemberId());
        stockDeal.setBrokerAccountId(stockOrder.getBrokerAccountId());
        stockDeal.setCode(vo.getInstrumentId());
        stockDeal.setName(vo.getInstrumentName());
        stockDeal.setCommission(calcCommission(
                stockOrder.getMemberId(),
                stockOrder.getBrokerAccountId(),
                vo.getTradedAmount()));

        if (StockOrderBO.ORDER_TYPE_BUY == stockOrder.getOrderType()) {
            // 买入开仓
            stockPositionService.open(vo, stockOrder);

            // 买入减少金额：成交额 + 手续费
            memberBrokerAccountService.reduceMoney(
                    stockOrder.getMemberId(),
                    stockOrder.getBrokerAccountId(),
                    vo.getTradedAmount().add(stockDeal.getCommission()));
        } else {
            // 卖出平仓
            StockPositionBO position = stockPositionService.close(vo, stockOrder);
            BigDecimal openPrice = position == null ? new BigDecimal(0) : position.getPrice();

            // 平仓金额：成交额 - 手续费
            BigDecimal closeMoney = vo.getTradedAmount().subtract(stockDeal.getCommission());
            // 开仓金额：持仓均价 * 平仓数量
            BigDecimal openMoney = openPrice.multiply(BigDecimal.valueOf(vo.getTradedVolume()));
            // 盈亏金额：平仓金额 - 开仓金额
            BigDecimal profitMoney = closeMoney.subtract(openMoney).setScale(2, RoundingMode.HALF_UP);

            stockDeal.setProfitMoney(profitMoney);

            memberBrokerAccountService.addMoney(
                    stockOrder.getMemberId(),
                    stockOrder.getBrokerAccountId(),
                    closeMoney,
                    profitMoney);
        }

        stockDealService.save(stockDeal);

        String message = stockOrder.getCode() + "[" + stockOrder.getName() + "]已成功成交，" +
                "成交均价[" + vo.getTradedPrice() + "]，" +
                "成交数量[" + vo.getTradedVolume() + "]，" +
                "成交金额[" + vo.getTradedAmount() + "]";
        stockOrderMessageService.save(stockOrder.getMemberId(), stockOrder.getAccountId(), message);
    }

    public BigDecimal calcCommission(String memberId, String brokerAccountId, BigDecimal money) {
        if (money == null) {
            money = new BigDecimal(0);
        }
        if (StringUtil.isBlank(memberId) || StringUtil.isBlank(brokerAccountId)) {
            return new BigDecimal(0);
        }

        MemberBrokerAccountBO account = memberBrokerAccountService.queryOneBy(memberId, brokerAccountId);
        if (account != null && account.getChargeRate() != null) {
            BigDecimal charge = money.multiply(account.getChargeRate());
            if (account.getChargeLowest() != null && charge.compareTo(account.getChargeLowest()) < 0) {
                charge = account.getChargeLowest();
            }
            return charge.setScale(2, RoundingMode.HALF_UP);
        } else if (account != null && account.getChargeLowest() != null) {
            return account.getChargeLowest().setScale(2, RoundingMode.HALF_UP);
        } else {
            return new BigDecimal(0);
        }
    }
}
